use palette::{Hsl, Hsv, Hwb, IntoColor, Lab, Lch, LinSrgb, Oklab, Oklch, Srgb, Xyz};

const TEST_COLORS: &[(f32, f32, f32, &str)] = &[
    (0., 0., 0., "black"),
    (1., 1., 1., "white"),
    (1., 0., 0., "red"),
    (0., 1., 0., "green"),
    (0., 0., 1., "blue"),
    (1., 1., 0., "yellow"),
    (1., 0., 1., "magenta"),
    (0., 1., 1., "cyan"),
    (0.5, 0.5, 0.5, "gray"),
    (0.5, 0.5, 0., "olive"),
    (0.5, 0., 0.5, "purple"),
    (0., 0.5, 0.5, "teal"),
    (0.5, 0., 0., "maroon"),
    (0., 0.5, 0., "lime"),
    (0., 0., 0.5, "navy"),
    (0.5, 0.5, 0., "orange"),
    (0.5, 0., 0.5, "fuchsia"),
    (0., 0.5, 0.5, "aqua"),
];

fn main() {
    println!(
        "// Generated by gen_tests. Do not edit.
#[cfg(test)]
use crate::{{Hsla, Hsva, Hwba, Srgba, LinearRgba, Oklaba, Oklcha, Laba, Lcha, Xyza}};

#[cfg(test)]
pub struct TestColor {{
    pub name: &'static str,
    pub rgb: Srgba,
    pub linear_rgb: LinearRgba,
    pub hsl: Hsla,
    pub hsv: Hsva,
    pub hwb: Hwba,
    pub lab: Laba,
    pub lch: Lcha,
    pub oklab: Oklaba,
    pub oklch: Oklcha,
    pub xyz: Xyza,
}}
"
    );

    println!("// Table of equivalent colors in various color spaces");
    println!("#[cfg(test)]");
    println!("pub const TEST_COLORS: &[TestColor] = &[");
    for (r, g, b, name) in TEST_COLORS {
        let srgb = Srgb::new(*r, *g, *b);
        let linear_rgb: LinSrgb = srgb.into_color();
        let hsl: Hsl = srgb.into_color();
        let hsv: Hsv = srgb.into_color();
        let hwb: Hwb = srgb.into_color();
        let lab: Lab = srgb.into_color();
        let lch: Lch = srgb.into_color();
        let oklab: Oklab = srgb.into_color();
        let oklch: Oklch = srgb.into_color();
        let xyz: Xyz = srgb.into_color();
        println!("    // {name}");
        println!(
            "    TestColor {{
        name: \"{name}\",
        rgb: Srgba::new({}, {}, {}, 1.0),
        linear_rgb: LinearRgba::new({}, {}, {}, 1.0),
        hsl: Hsla::new({}, {}, {}, 1.0),
        hsv: Hsva::new({}, {}, {}, 1.0),
        hwb: Hwba::new({}, {}, {}, 1.0),
        lab: Laba::new({}, {}, {}, 1.0),
        lch: Lcha::new({}, {}, {}, 1.0),
        oklab: Oklaba::new({}, {}, {}, 1.0),
        oklch: Oklcha::new({}, {}, {}, 1.0),
        xyz: Xyza::new({}, {}, {}, 1.0),
    }},",
            VariablePrecision(srgb.red),
            VariablePrecision(srgb.green),
            VariablePrecision(srgb.blue),
            VariablePrecision(linear_rgb.red),
            VariablePrecision(linear_rgb.green),
            VariablePrecision(linear_rgb.blue),
            VariablePrecision(hsl.hue.into_positive_degrees()),
            VariablePrecision(hsl.saturation),
            VariablePrecision(hsl.lightness),
            VariablePrecision(hsv.hue.into_positive_degrees()),
            VariablePrecision(hsv.saturation),
            VariablePrecision(hsv.value),
            VariablePrecision(hwb.hue.into_positive_degrees()),
            VariablePrecision(hwb.whiteness),
            VariablePrecision(hwb.blackness),
            VariablePrecision(lab.l / 100.0),
            VariablePrecision(lab.a / 100.0),
            VariablePrecision(lab.b / 100.0),
            VariablePrecision(lch.l / 100.0),
            VariablePrecision(lch.chroma / 100.0),
            VariablePrecision(lch.hue.into_positive_degrees()),
            VariablePrecision(oklab.l),
            VariablePrecision(oklab.a),
            VariablePrecision(oklab.b),
            VariablePrecision(oklch.l),
            VariablePrecision(oklch.chroma),
            VariablePrecision(oklch.hue.into_positive_degrees()),
            VariablePrecision(xyz.x),
            VariablePrecision(xyz.y),
            VariablePrecision(xyz.z),
        );
    }
    println!("];");
}

struct VariablePrecision(f32);

impl std::fmt::Display for VariablePrecision {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        if self.0.fract() == 0.0 {
            return write!(f, "{}.0", self.0);
        }
        write!(f, "{}", self.0)
    }
}
